﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using CS.Windows.Controls;
using DeepForest.Phone.Assets.Tools;
using Lasalara.Model;
using Lasalara.View;
using Lasalara.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Net.NetworkInformation;
using Microsoft.Phone.Shell;
using RestSharp;
using ServiceStack.Text;

namespace Lasalara.ViewModel
{
    public class BookViewModel : GlobalViewModel
    {
        public RestRequestAsyncHandle RequestHandle;

        #region webRequests

        /// <summary>
        /// Deletes book
        /// </summary>
        /// <param name="bookKey">Book key</param>
        /// <param name="user">User to download for</param>
        /// <param name="callback">Callback method</param>
        public void DeleteBookWebRequest(string bookKey, User user, Action<RestResponse, string> callback)
        {
            var userKey = user.UserKey;
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
                              {
                                  Resource = "delete",
                              };
            request.AddParameter("k", bookKey);
            request.AddParameter("pk", userKey);
            RequestHandle = client.ExecuteAsync(request, (response) => callback(response as RestResponse, bookKey));
        }

        /// <summary>
        /// Changes password
        /// </summary>
        /// <param name="user">The user changing password</param>
        /// <param name="oldPassword">Users old password</param>
        /// <param name="newPassword">Users new password</param>
        /// <param name="callback">Callback method</param>
        public void ChangePasswordWebRequest(User user, string oldPassword, string newPassword,
                                             Action<RestResponse, string> callback)
        {
            var userKey = user.UserKey;
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
                              {
                                  Resource = "newpass",
                              };
            request.AddParameter("uk", userKey);
            request.AddParameter("op", oldPassword);
            request.AddParameter("np", newPassword);
            RequestHandle = client.ExecuteAsync(request, (response) => callback(response as RestResponse, newPassword));
        }

        /// <summary>
        /// Downloads book
        /// </summary>
        /// <param name="book">Book that is being downloaded</param>
        /// <param name="callback">Callback method</param>
        public void DownloadBookWebRequest(Book book,Action<RestResponse, Book> callback)
        {
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
                              {
                                  Resource = "getmchapters",
                                  RequestFormat = DataFormat.Json
                              };
            request.AddParameter("bk", book.Bk);
            RequestHandle = client.ExecuteAsync(request, (response) => callback(response as RestResponse, book));
        }

        /// <summary>
        /// Add book
        /// </summary>
        /// <param name="email">Book author's email</param>
        /// <param name="title">Book title</param>
        /// <param name="user">User to add book for</param>
        /// <param name="callback">Callback method</param>
        public void AddBookWebRequest(string email, string title, User user, Action<RestResponse> callback)
        {
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
                              {
                                  Resource = "addbook",
                                  RequestFormat = DataFormat.Json
                              };
            request.AddParameter("em", email);
            request.AddParameter("bt", title);
            request.AddParameter("uk", user.UserKey);
            RequestHandle = client.ExecuteAsync(request, (response) => callback(response as RestResponse));
        }

        /// <summary>
        /// Update user's books from the server
        /// </summary>
        /// <param name="email">Users email</param>
        /// <param name="password">Users password</param>
        /// <param name="callback">Callback method</param>
        public void UpdateBooksWebRequest(string email, string password, Action<RestResponse> callback)
        {
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
                              {
                                  Resource = "signin",
                                  RequestFormat = DataFormat.Json
                              };
            request.AddParameter("em", email);
            request.AddParameter("pw", password);
            RequestHandle = client.ExecuteAsync(request, (response) => callback(response as RestResponse));
        }

        /// <summary>
        /// Find if chapter has been updated
        /// </summary>
        /// <param name="book">book that needs to be checked</param>
        /// <param name="callback">Callback method</param>
        public void PingChaptersWebRequest(Book book, Action<RestResponse, Book> callback)
        {
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
            {
                Resource = "pingchapters",
                RequestFormat = DataFormat.Json
            };
            request.AddParameter("bk", book.Bk);
            RequestHandle = client.ExecuteAsync(request, (response) => callback(response as RestResponse, book));
        }


        /// <summary>
        /// Get all questions for chapter
        /// </summary>
        /// <param name="chapter">Chapter to get the questions for</param>
        /// <param name="callback">Callback method</param>
        /// <param name="version">The new version for the chapter</param>
        public void GetQuestionsWebRequest(Chapter chapter,string version, Action<RestResponse, Chapter, string> callback)
        {
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
                              {
                                  Resource = "getquestions",
                                  RequestFormat = DataFormat.Json
                              };
            request.AddParameter("ck", chapter.ChapterKey);
            RequestHandle = client.ExecuteAsync(request, (response) => callback(response as RestResponse, chapter, version));
        }

        /// <summary>
        /// Get all questions for chapter async
        /// </summary>
        /// <param name="chapter">Chapter to get the questions for</param>
        public static Task<IRestResponse> GetQuestionsWebRequestAsync(Chapter chapter, CancellationToken token)
        {
            var client = new RestClient(App.BaseUrl);
            var request = new RestRequest(Method.POST)
            {
                Resource = "getquestions",
                RequestFormat = DataFormat.Json
            };
            request.AddParameter("ck", chapter.ChapterKey);
            return !token.IsCancellationRequested ? client.ExecuteAsync(request, token) : null;

        }
        #endregion
        /// <summary>
        /// Return list of books from the supplied dictionary
        /// </summary>
        /// <param name="data">The Dictionary containing the values</param>
        public static List<Book> GetBooksFromDictionary(Dictionary<string, string> data)
        {
            if (data != null && data.ContainsKey("books"))
            {
                var listStr = JsonSerializer.DeserializeFromString<List<string>>(data["books"]);
                var books = listStr.Select(JsonSerializer.DeserializeFromString<Dictionary<string, string>>).
                    Select(bookDictionary => new Book
                                                 {
                                                     Title = bookDictionary["title"],
                                                     Bk = bookDictionary["bk"],
                                                     Email = bookDictionary["email"],
                                                     Name = bookDictionary["name"],
                                                     Dr = bookDictionary["dr"],
                                                     Institution = bookDictionary["institution"]
                                                 });

                return books.ToList();
            }
            return null;
        }

        /// <summary>
        /// Return book from supplied dictionary
        /// </summary>
        /// <param name="data">The Dictionary containing the book values</param>
        public static Book GetBookFromDictionary(Dictionary<string, string> data)
        {
            var book = new Book
                           {
                               Title = data["title"],
                               Bk = data["bk"],
                               Email = data["email"],
                               Name = data["name"],
                               Dr = data["dr"],
                               Institution = data["institution"]
                           };
            return book;
        }

        /// <summary>
        /// Set supplied pivotitem as selected and remove other pages.
        /// </summary>
        /// <param name="main">The main pivot control</param>
        /// <param name="item">The pivot page that needs to be navigated to</param>
        /// <param name="permanent">The pivot page that always needs to be available</param>
        public static void CheckAndSelectPivotItem(Pivot main, PivotItem item, PivotItem permanent)
        {
            if (!main.Items.Contains(item))
            {
                //removes only items that aren't "permanent"
                if (main.SelectedItem != permanent)
                {
                    main.Items.Remove(main.SelectedItem);
                }
                main.Items.Add(item);
            }
            main.SelectedItem = item;
        }

        /// <summary>
        /// Validate change password fields and return true if webrequest can be made.
        /// </summary>
        public static bool ValidateChangePassword(WatermarkPasswordBox oldPass, WatermarkPasswordBox newPass)
        {
            if (oldPass.Password != null && newPass.Password != null && !String.IsNullOrEmpty(oldPass.Password) &&
                !String.IsNullOrEmpty(newPass.Password))
            {
                if (!oldPass.Password.Equals(newPass.Password))
                {
                    if (oldPass.Password.Length > 3)
                    {
                        if (DeviceNetworkInformation.IsNetworkAvailable)
                        {
                            return true;
                        }
                        ShowToast(AppResources.Error, AppResources.CannotChangePassNoInternet);
                    }
                    else
                    {
                        ShowToast(AppResources.Error, AppResources.PasswordTooShort);
                    }
                }
                else
                {
                    ShowToast(AppResources.Error, AppResources.OldAndNewPasswordMatch);
                }
            }
            else
            {
                ShowToast(AppResources.Error, AppResources.EnterOldAndNewPassword);
            }
            return false;
        }

        /// <summary>
        /// Remove pivotpages that should not be loaded
        /// </summary>
        /// <param name="pivot">Where to remove the pivot from</param>
        /// <param name="itemsToRemove">List of items that need to be removed</param>
        public static void RemovePivotPages(Pivot pivot, IEnumerable<PivotItem> itemsToRemove)
        {
            foreach (var item in itemsToRemove)
            {
                pivot.Items.Remove(item);
            }
        }

        /// <summary>
        /// Update book progress based on the individual chapter progresses
        /// </summary>
        /// <param name="book">The book to update</param>
        public static void UpdateBookProgress(Book book)
        {
            var totalQuestions = 0;
            var totalAnswered = 0;
            foreach (var chptr in book.Chapters.Where(x => x.IsDownloaded))
            {
                totalQuestions += chptr.Questions.Count;
                totalAnswered += chptr.Questions.Count(x => x.AnsweredDate > DateTime.Now);
            }
            var bookProgress = 0.0;
            if (totalQuestions != 0)
            {
                bookProgress = (double)totalAnswered / totalQuestions * 100;
            }
            if (bookProgress > 100) { bookProgress = 100; }
            book.Progress = bookProgress;
        }

        /// <summary>
        /// Insert the supplied chapter to the supplied users correct book and the chapters listbox
        /// </summary>
        /// <param name="user">The user to add the chapter to</param>
        /// <param name="chapter">The chapter to add</param>
        public static Book InsertChapterToUserBooks(User user, Chapter chapter)
        {
            foreach (var book in user.DownloadedUserBooks.Where(book => !book.Chapters.Contains(chapter)))
            {
                foreach (var chap in book.Chapters.Where(chap => !chap.Equals(chapter)))
                {
                    book.Chapters[book.Chapters.IndexOf(chap)] = chapter;
                }
                break;
            }
            return null;
        }

        /// <summary>
        /// Toggles the checkbox and book progress percentage visibility by checking if the book is downloaded.
        /// </summary>
        /// <param name="source">The ui object to search the checkboxes from</param>
        /// <param name="visible">Whether to show or hide checkboxes</param>
        /// <param name="filter">Whether to show checkboxes for books that are not downloaded</param>
        /// <param name="show">Determines if checkboxes should be shown for all items or only items that are downloaded</param>
        public static void ShowCheckBoxes(DependencyObject source ,bool visible, bool filter, bool show = true)
        {
            var count = VisualTreeHelper.GetChildrenCount(source);
            for (var i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(source, i);
                var targetItem = (FrameworkElement) child;
                if (filter)
                {
                    if (targetItem.GetType().Name == "ListBoxItem")
                    {
                        var book = targetItem.DataContext as Book;
                        if (!book.IsDownloaded)
                        {
                            show = false;
                        }
                    }
                }
                if (targetItem.Name == "checkBox")
                {
                    if (show)
                    {
                        // Show checkboxes only for items that are downloaded. Also clear all the checkboxes.
                        var emptyBox = new Uri("../Images/basecircle.png", UriKind.Relative);
                        var checkBox = targetItem as System.Windows.Shapes.Rectangle;
                        var mask = checkBox.OpacityMask as ImageBrush;
                        var image = mask.ImageSource as BitmapImage;
                        image.UriSource = emptyBox;
                        checkBox.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
                    }
                    show = true;
                }
                if (targetItem.Name == "bookProgressText")
                {
                    var progress = targetItem as TextBlock;
                    progress.Visibility = visible ? Visibility.Collapsed : Visibility.Visible;
                }
                ShowCheckBoxes(targetItem, visible, filter, show);
            }
        }

        /// <summary>
        /// Toggles the checkbox in the library view.
        /// </summary>
        public static void ToggleCheckBox(DependencyObject clicked)
        {
            var count = VisualTreeHelper.GetChildrenCount(clicked);
            for (var i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(clicked, i);
                var targetItem = (FrameworkElement)child;
                if (targetItem.Name == "checkBox")
                {
                    var checkedBox = new Uri("../Images/check.png", UriKind.Relative);
                    var emptyBox = new Uri("../Images/basecircle.png", UriKind.Relative);

                    var checkbox = targetItem as System.Windows.Shapes.Rectangle;
                    var mask = checkbox.OpacityMask as ImageBrush;
                    var source = mask.ImageSource as BitmapImage;
                    source.UriSource = source.UriSource == emptyBox ? checkedBox : emptyBox;
                    return;
                }
                ToggleCheckBox(targetItem);
            }
        }

        /// <summary>
        /// Enables or disables all application bar buttons.
        /// </summary>
        public static void EnableAppBarIcons(bool enabled)
        {
            Books.LogOutItem.IsEnabled = enabled;
            Books.UpdateBookListItem.IsEnabled = enabled;
            Books.ChangePasswordItem.IsEnabled = enabled;
            Books.OpenTutorialItem.IsEnabled = enabled;

            Books.SyncButton.IsEnabled = enabled;
            Books.AddButton.IsEnabled = enabled;
            Books.DeleteButton.IsEnabled = enabled;
            Books.ResetButton.IsEnabled = enabled;
        }

        /// <summary>
        /// Changes or unchanges delete button to confirm delete on app bar.
        /// </summary>
        /// <param name="enable">Whether to show or hide confirm delete option</param>
        public static void EnableDelete(bool enable)
        {
            Books.DeleteButton.IconUri = enable ? new Uri("/Images/appbar.check.rest.png", UriKind.Relative) : new Uri("/Images/appbar.delete.rest.png", UriKind.Relative);
        }

        /// <summary>
        /// Changes or unchanges sync button to confirm sync on app bar.
        /// </summary>
        /// <param name="enable">Whether to show or hide confirm sync option</param>
        public static void EnableSync(bool enable)
        {
            Books.SyncButton.IconUri = enable ? new Uri("/Images/appbar.check.rest.png", UriKind.Relative) : new Uri("/Images/appbar.sync.rest.png", UriKind.Relative);
        }

        /// <summary>
        /// Changes or unchanges reset button to confirm reset on app bar.
        /// </summary>
        /// <param name="enable">Whether to show or hide confirm reset option</param>
        public static void EnableReset(bool enable)
        {
            Books.ResetButton.IconUri = enable ? new Uri("/Images/appbar.check.rest.png", UriKind.Relative) : new Uri("Images/appbar.reset.png", UriKind.Relative); 
        }

        /// <summary>
        /// Enables or disables the supplied listbox
        /// </summary>
        public static void EnableListBox(bool enable, ListBox listBox)
        {
            listBox.IsEnabled = enable;
        }
    }
}